package cn.edu.hznu.spring;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

import org.apache.commons.digester3.Digester;
import org.xml.sax.SAXException;

public class SpringByXml {
	private Digester digester;
	private Map<String, Bean> beanMap;
	
	public void load() throws IOException, SAXException {
		Digester digester = new Digester();
		digester.addObjectCreate("beans", Beans.class);
		digester.addObjectCreate("beans/bean", Bean.class);
		
		digester.addSetProperties("beans/bean", "class", "className");
		digester.addSetProperties("beans/bean");
		
		digester.addObjectCreate("beans/bean/property", Property.class);
		digester.addSetProperties("beans/bean/property");
		
		digester.addSetNext("beans/bean/property", "addProperty");   
		digester.addSetNext("beans/bean", "addBean");
		
//		InputStream in = ClassLoader.getSystemResourceAsStream("beas.xml");
//		InputStream in = this.getClass().getClassLoader().getResourceAsStream("");	
		InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream("beans.xml"); 
//		InputStream in = this.getClass().getResourceAsStream("beans.xml");
		
		if (in == null) {
			in = new FileInputStream("./src/main/java/cn/edu/hznu/spring/beans.xml");
		}
		Beans beans = digester.parse(in);
		beanMap = beans.getBeans()
				       .stream()
				       .collect(Collectors.toMap(Bean::getId, bean -> bean));
	}
	
	private String upLowerFirstCharOfString(String str) {
		char firstCharAfterUpLower = (char) (str.charAt(0) - 32);
		return firstCharAfterUpLower + str.substring(1);
	}
	
	public Object getBean(String beanId) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
		Bean bean = beanMap.get(beanId);
		Class clazz = Class.forName(bean.getClassName());
		Object obj = clazz.newInstance();
		bean.getProps()
			.forEach(prop -> {
				String methodName = "set" + upLowerFirstCharOfString(prop.getName());
				Optional.ofNullable(prop.getValue())
						.ifPresent(value -> {
							try {
								Method m = clazz.getMethod(methodName, String.class);
								m.invoke(obj, value);
							} catch (Exception e) {
								e.printStackTrace();
							}
						});
				Optional.ofNullable(prop.getRef())
						.ifPresent(ref -> {
							try {
								Bean refBean = beanMap.get(ref);
								Class refClazz = Class.forName(refBean.getClassName());
								Method m = clazz.getMethod(methodName, refClazz);
								Object refObj = getBean(refBean.getId());
								m.invoke(obj, refObj);
							} catch (Exception e) {
								e.printStackTrace();
							}
							
						});
			});
		return obj;
	}
	
	public static void main(String[] args) {
		SpringByXml spring = new SpringByXml();
		try {
			spring.load();
			BeanA beanA = (BeanA)spring.getBean("beanA");
			System.out.println(beanA.getTitle());
			System.out.println(beanA.getBeanB().getTitle());
			
		} catch (ClassNotFoundException | InstantiationException |
				IllegalAccessException | IOException | SAXException e) {
			e.printStackTrace();
		}
	}
		

}
